package com.nulldev.util.concurrency.threadIt.v3.executors;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.Collection;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.concurrency.threadIt.ThreadUtils;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.ExecutorFeatureExtensions;
import com.nulldev.util.concurrency.threadIt.v4.emapi.extensions.ExtendedExecutor;
import com.nulldev.util.data.FinalBypass;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.manage.Unimplemented;

@Deprecated
public class Lightning extends AbstractExecutorService implements ExecutorService, ExtendedExecutor {
	private static final Logger log = LoggerFactory.getLogger();
	public static final int DEFAULT_AMOUNT = Arguments.hasArgument("--Lightning.legacyDefault") ? 128 : 64;

	private volatile boolean running;
	private volatile boolean stopped;
	private final int threadCount;
	private final BlockingQueue<Runnable> queue;
	private final Thread[] threads;

	public Lightning() {
		this(DEFAULT_AMOUNT);
	}

	public Lightning(final int threadCount) {
		if (JVM.version() <= 1.7)
			throw new UnsupportedOperationException("Lightning does not run on pre JDK 8 enviroments.");
		this.threadCount = threadCount;
		this.queue = new LinkedBlockingQueue<Runnable>();
		this.running = true;
		this.stopped = false;
		this.threads = new Thread[threadCount];
		for (int i = 0; i < threadCount; i++) {
			this.threads[i] = new Thread(new Worker(), "Lightning: Executor #" + i);
			this.threads[i].start();
		}
	}

	@Override
	public void execute(Runnable runnable) {
		try {
			if (!this.queue.offer(runnable, 300, TimeUnit.MILLISECONDS)) {
				throw new IllegalStateException("execute: Cannot add element: " + runnable);
			}
		} catch (Exception ie) {
			throw new RuntimeException(ie);
		}
	}

	@Override
	public void executor_submitAll(final List<Runnable> r) {
		if (r == null || r.isEmpty())
			return;
		try {
			this.queue.addAll(r);
		} catch (Exception ie) {
			throw new RuntimeException(ie);
		}
	}

	@Override
	public void executor_drainTasks(final Queue<Runnable> async_queue, final int batchSize) {
		if (async_queue == null || async_queue.isEmpty())
			return;
		for (int i = 0; i < batchSize; i++) {
			try {
				final Runnable r = async_queue.poll();
				if (r == null)
					continue;
				if (!this.queue.offer(r, 300, TimeUnit.MILLISECONDS)) {
					throw new IllegalStateException("drainTasks: Cannot add element: " + r);
				}
			} catch (InterruptedException e) {
				continue;
			}
		}
	}

	private class Worker implements Runnable {

		@Override
		public void run() {
			while (running) {
				Runnable runnable = null;
				try {
					runnable = queue.take();
				} catch (InterruptedException ie) {
					// was interrupted - just go round the loop again,
					// if running = false will cause it to exit
				}
				try {
					if (runnable != null) {
						runnable.run();
					}
				} catch (Throwable e) {
					log.error("Task failed.", e);
				}
			}
		}
	}

	private static final ExecutorFeatureExtensions[] SUPPORTED = new ExecutorFeatureExtensions[]
		{ ExecutorFeatureExtensions.EEX_core_executor_slots, ExecutorFeatureExtensions.EEX_query_slots_supported,
				ExecutorFeatureExtensions.EEX_core_submit_all_tasks, ExecutorFeatureExtensions.EEX_core_drain_task_queue,
				ExecutorFeatureExtensions.EEX_executor_await_idle, ExecutorFeatureExtensions.EEX_query_batching_supported,
				ExecutorFeatureExtensions.EEX_query_slots_supported, };

	@Override
	public ExecutorFeatureExtensions[] supported() {
		return SUPPORTED;
	}

	@Override
	public boolean supportsFeature(final ExecutorFeatureExtensions feature) {
		return ArrayUtils.contains(SUPPORTED, feature);
	}

	@Override
	public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
		this.queue.clear();
		ThreadUtils.kill(this.threads);
		this.running = false;
		return this.stopped = true;
	}

	@Override
	@Unimplemented
	public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
		throw new UnsupportedOperationException("invokeAll(tasks, timeout, unit) -> Not implemented!");
	}

	@Override
	@Unimplemented
	public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
		throw new UnsupportedOperationException("invokeAny(tasks) -> Not implemented!");
	}

	@Override
	@Unimplemented
	public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
			throws InterruptedException, ExecutionException, TimeoutException {
		throw new UnsupportedOperationException("invokeAny(tasks, timeout, unit) -> Not implemented!");
	}

	@Override
	public boolean isShutdown() {
		return this.stopped;
	}

	@Override
	public boolean isTerminated() {
		return this.stopped;
	}

	@Override
	public void shutdown() {
		this.queue.clear();
		ThreadUtils.kill(this.threads);
		this.running = false;
		this.stopped = true;
		// throw new UnsupportedOperationException("shutdown() -> Not implemented!");
	}

	@Override
	@Unimplemented
	public List<Runnable> shutdownNow() {
		throw new UnsupportedOperationException("shutdownNow() -> Not implemented!");
	}

	@Override
	@Unimplemented
	public <T> Future<T> submit(Callable<T> task) {
		final FinalBypass<T> value = new FinalBypass<T>(null);
		final FinalBypass<Boolean> done = new FinalBypass<Boolean>(false);
		final Runnable r = new Runnable() {

			@Override
			public void run() {
				try {
					value.setObject(task.call());
					done.setObject(true);
				} catch (Exception e) {
					done.setObject(true);
					throw new RuntimeException(e);
				}
			}
		};
		final Future<T> future = new Future<T>() {

			@Override
			@Unimplemented
			public boolean cancel(boolean var1) {
				return false;
			}

			@Override
			public T get() throws InterruptedException, ExecutionException {
				if (!done.getObject()) {
					while (!done.getObject()) {
						Thread.sleep(20L);
					}
				}
				return value.getObject();
			}

			@Override
			@Unimplemented
			public T get(long var1, TimeUnit var3) throws InterruptedException, ExecutionException, TimeoutException {
				if (!done.getObject()) {
					while (!done.getObject()) {
						Thread.sleep(20L);
					}
				}
				return value.getObject();
			}

			@Override
			@Unimplemented
			public boolean isCancelled() {
				return false;
			}

			@Override
			public boolean isDone() {
				return done.getObject();
			}
		};
		this.execute(r);
		// throw new UnsupportedOperationException("submit(task) -> Not implemented!");
		return future;
	}

	@Override
	public Future<?> submit(Runnable task) {
		this.execute(task);
		return null;
	}

	@Override
	@Unimplemented
	public <T> Future<T> submit(Runnable task, T result) {
		throw new UnsupportedOperationException("submit(task, T) -> Not implemented!");
	}

	public int getThreadCount() {
		return this.threadCount;
	}

	public int getActiveThreads() {
		return this.threads.length;
	}

	public void renderDebuggingUI(final Graphics2D g2d) {
		g2d.setComposite(AlphaComposite.SrcOver.derive(0.6f));
		g2d.setColor(Color.BLACK);
		g2d.fillRect(0, 0, 90, 50);
		g2d.setColor(Color.WHITE);
		g2d.setComposite(AlphaComposite.SrcOver.derive(1.0f));
		g2d.setFont(g2d.getFont().deriveFont((float) 12));
		g2d.drawString("Lightning 3.0", 5, 15);
		g2d.drawString("Threads: " + this.getThreadCount(), 5, 30);
		g2d.drawString("Active: " + this.getActiveThreads(), 5, 45);
	}

	public Runnable firstTask() {
		return this.queue.peek();
	}

	public Runnable lastTask() {
		return this.queue.peek();
	}

	@Override
	public String toString() {
		return "Lightning@" + this.hashCode() + "[threads=" + this.threadCount + ",active=" + this.threads.length + "]";
	}

	@Override
	public void awaitIdle() {
		final int limit = 8;
		int waitingInt = limit;
		final int s = (int) (35 / limit);
		while (!this.queue.isEmpty()) {
			try {
				if (waitingInt <= 0) {
					waitingInt = limit + 1;
				}
				Thread.sleep((long) Math.ceil(s / waitingInt));
				waitingInt -= 1;
			} catch (InterruptedException e) {
			}
		}
	}
}
